package dao

import (
	"douyin/model"
	"douyin/utils/cache"
	"errors"
	"log"
	"strconv"
	"sync"

	"gorm.io/gorm"
)

type videoDAO struct{}

const kMAXVideoCount = 30

var (
	videoDAOInstance *videoDAO
	videoDAOOnce     sync.Once
)

// NewVideo 单例模式创建videoInstance
func NewVideo() *videoDAO {
	videoDAOOnce.Do(func() {
		videoDAOInstance = &videoDAO{}
	})

	return videoDAOInstance
}

// Create 新增数据
func (*videoDAO) Create(values map[string]interface{}) error {
	err := db.Model(&model.Video{}).Create(values).Error
	if err != nil {
		log.Print("新增视频实录失败")
	}
	return err
}

// QueryVideos 根据参数中的条件查询视频
func (*videoDAO) QueryVideos(conditions map[string]interface{}, fields ...string) ([]model.Video, error) {
	var v []model.Video
	err := db.Model(&model.Video{}).
		Select(fields).
		Where(conditions).
		Find(&v).Error
	if err != nil {
		return nil, err
	}

	return v, nil
}

// QueryLatest 查询最新的若干条视频，但保证不超过 latestTime
func (*videoDAO) QueryLatest(latestTime string) ([]model.Video, error) {
	var v []model.Video
	err := db.Model(&model.Video{}).
		Order("create_at desc").
		Select([]string{"author_id", "play_url", "cover_url", "favorite_count", "comment_count", "create_at", "video_id"}).
		Where("create_at < ?", latestTime).
		Find(&v).
		Limit(kMAXVideoCount).
		Error

	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	return v, nil
}

// AddFavorite 增加 count 数量的点赞数
// count 参数主要为了更好的扩展性
// 使用 count 可以批量更新减少更新时带来的一些问题
func (*videoDAO) AddFavorite(videoID int64, count int64) error {
	err := db.Model(model.Video{}).
		Where("video_id = ?", videoID).
		Update("favorite_count", gorm.Expr("favorite_count + ?", count)).
		Error

	if err != nil {
		return err
	}
	return nil
}

// SubFavorite  减少 count 数量的点赞数
// count 参数主要为了更好的扩展性
// 使用 count 可以批量更新减少更新时带来的一些问题
func (*videoDAO) SubFavorite(videoID int64, count int64) error {
	err := db.Model(model.Video{}).
		Where("video_id = ? ", videoID).
		Update("favorite_count", gorm.Expr("favorite_count + ?", count)).
		Error

	if err != nil {
		return err
	}
	return nil
}

// QueryVideo 根据单个videoID查询单个视频信息
// 返回一个视频的信息
func (*videoDAO) QueryVideo(videoID int64, fields ...string) (model.Video, error) {
	var v model.Video
	err := db.Model(&model.Video{}).
		Select(fields).
		Where("video_id = ?", videoID).
		Error

	if err != nil {
		return model.Video{}, err
	}
	return v, nil
}

// UpdateVideoInfo 更新视频信息，将会更新 video 中所有非空字段
func (*videoDAO) UpdateVideoInfo(video model.Video) error {
	return db.Model(model.Video{}).Where("video_id=?", video.VideoID).Updates(&video).Error
}

// QueryVideosByID 通过 ID 列表批量查询视频
func (*videoDAO) QueryVideosByID(IDList []int64, fields ...string) ([]model.Video, error) {
	var v []model.Video
	err := db.Model(&model.Video{}).
		Select(fields).
		Where("video_id IN ? ", IDList).
		Find(&v).
		Error
	// 处理错误和未查询到记录的err
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	return v, nil
}

// AddComment 增加评论数量，直接操作数据库，不经过缓存
func (*videoDAO) AddComment(videoID int64, count int) error {
	err := db.Model(&model.Video{}).
		Where("video_id = ? ", videoID).
		Update("comment_count", gorm.Expr("comment_count + ?", count)).
		Error

	return err
}

// SubComment 减少评论数量，直接操作数据库，不经过缓存
func (*videoDAO) SubComment(videoID int64, count int) error {
	err := db.Model(&model.Video{}).
		Where("video_id = ? ", videoID).
		Update("comment_count", gorm.Expr("comment_count - ?", count)).
		Error

	return err
}

func (*videoDAO) QueryFavoriteCountFromCache(videoID int64) int64 {
	// 暂未对缓存实例是否存在进行处理，严格来说应该先判断map 中是否存在
	if view, err := cacheGroup["favorite"].Get(strconv.FormatInt(videoID, 10)); err == nil {
		return cache.BytesToInt64(view.ByteSlice())
	}
	return 0
}

func (v *videoDAO) updateFavoriteCountToCache(videoID int64, count int64) {
	cacheGroup["favorite"].Set(strconv.FormatInt(videoID, 10), cache.Int64ToBytes(count))
}

// IncreaseFavoriteByCache  增加缓存里面的点赞数量
func (*videoDAO) IncreaseFavoriteByCache(videoID int64, delta int64) {
	count := NewVideo().QueryFavoriteCountFromCache(videoID)
	NewVideo().updateFavoriteCountToCache(videoID, count+delta)
}

// DecreaseFavoriteByCache  减少缓存里面的点赞数量
func (*videoDAO) DecreaseFavoriteByCache(videoID int64, delta int64) {
	count := NewVideo().QueryFavoriteCountFromCache(videoID)
	NewVideo().updateFavoriteCountToCache(videoID, count-delta)
}
